/**
 * 变量名命名规则：
 * 1. 英文字母 + 数字 + _ $
 * 2. 英文字母区分大小写
 * 3. 数字不能作为开头
 * 4. 可以是 Uni-Code 字符
 * 5. 需要连续，不能有空格等特殊字符
 */
let 中文 = 123;

// 单行注释
/**
 * 多行注释
 */

/**
 * 声明变量的三种方式
 */
// 变量，值可修改
var v = 1;
let l = 2;
// 常量，值不可修改
const c = 3;

/**
 * 作用域
 * 1. 全局作用域
 * 2. 函数作用域
 * 3. 块级作用域
 *
 * 特征
 * 1. 大作用域不可以访问小作用域的变量、函数
 * 2. 小作用域可以访问大作用域的变量、函数
 * 3. 函数、块级作用域之间相互隔离
 *
 * 什么是全局变量
 * 1. 声明在全局作用域的变量，就是全局变量
 * 2. 可以在任意位置访问
 *
 * 在相同作用于下，变量、函数可以同名吗？
 * 1. 不可以
 */

/**
 * boolean Boolean = true | false
 * string String = '' | "" | ``
 * number Number = 123 | 3.14
 * bigint BigInt = 9007199254740992n
 * symbol Symbol = Symbol("Howdy") !== Symbol("Howdy")
 * null Object = null 空值
 * undefined Undefined = undefined 未定义
 * object Object = {} 对象 | [] 数组对象
 */
let n1 = +'1';
let n2 = Number('1');
let n3 = '2' - 1;
let n4 = '2' * 3;
let n5 = '3' / 3;
let n6 = parseInt('12.3', 10); // 12
let n7 = parseFloat('3.14'); // 3.14

/**
 * 字面量
 * boolean 布尔值字面量
 * string 字符串字面量
 * number 数字字面量
 *
 * Object {} 对象字面量
 * Array [] 数组字面量
 * RegExp /\d+/g 正则字面量
 */
let b = false;
let s = 'bob';
let num10 = 0; // 117, 123456789123456789n（十进制，基数 10）
let num8 = 0o15; // 0o001, 0o777777777777n（八进制，基数 8）
let num16 = 0x1123; // 0x00111, 0x123456789ABCDEFn（十六进制，“hex”或基数 16）
let num2 = 0b11; // 0b0011, 0b11101001010101010101n（二进制，基数 2）

// 科学计数法
let float1 = 3.1e+3; // 0.0031
let float2 = 3.1e-3; // 3100

let obj = {
    name: 'bob',
    age: 12,
    sex: false,
    info: {
        info: {},
    },
    arr: [],
    fn() {
    },
    fn2: () => {
    },
}
let arr = [1, '2', false, {}, []]
let regExp = /\d+/g

let stringObject = `{
    "name": "bob"
}`
let strMini = "{\"name\":\"bob\"}"
// 字符串转对象
JSON.parse(strMini)
// 对象转字符串
JSON.stringify({name: 'bob'});

/**
 * if | else if | else
 * for | for in | for of | break | continue
 * while
 * switch | case | break | default
 * throw 抛出异常
 * try { 有可能出现异常的代码块 }
 * catch(e) { 捕获异常代码块 }
 * finally { 无论是否捕获都会执行的代码块 }
 * 递增语句：i++; ++i; i--; --i; i+=8; i-=8;
 */

/**
 * 函数声明
 */
function square(num) {
    return num * num;
}

// 引用变量（引用类型）Object类型都是引用类型
const obj2 = {};
const arr2 = [45];

function myFunc(theArr) {
    // 函数内部修改了引用类型的变量，引用类型本身的值会变吗？会变。
    theArr[0] = 30;
}

console.log(arr2[0]); // 45
myFunc(arr2);
console.log(arr2[0]); // 30

// 函数表达式
const fn1 = function fn2() {
    // fn1 === fn2 等价
}
// 箭头函数的this指向上一层作用域
const fn3 = () => {
}

// 递归调用: 用来遍历树状解构的对象（递进 + 回归 = 递归）
function loop(node) {
    console.log(node);

    if (node && node.children) {
        for (const childNode of node.children) {
            loop(childNode);
        }
    }
}

// 调用 loop 函数
loop();

class Animal {
    constructor() {
    }
}

// 调用 Animal 类的 构造函数
new Animal();


for (let i = 0; i < 10; i++) {
    // 小作用域的变量名，不要与大作用域的变量名重合，所以不能再用 i
    for (let j = 0; j < 10; j++) {
    }
}


// 隐式参数
function fn10() {
    const name = arguments[0];
    const args = [arguments[1], arguments[2], arguments[3]];
}

// 可选参数(剩余参数)
function fn11(name, ...args) {
    // args 可选参数（可以传，也可以不传，传的时候可以传任意数量的参数）
    fn11('bob', 1, 3, 5, 7, 8);
    // 可选参数范围 < 隐式参数范围
    // 隐式参数实际上包含所有参数
}

eval('') // 执行任意js代码
isFinite(100 / 0) // false 判断是否是有限的
isNaN('b') // true 判断传入的值是否为数字
parseFloat(); // 将字符串转成小数
parseInt(); // 将字符串转成整数

/**
 * 运算符
 * 1. 赋值运算符
 */
let varName; // 声明变量，
varName = 2; // 并赋值123

let varName1 = 2; // 声明变量, 并赋值123

// 加等运算
varName1 += 2;
// 减等运算
varName1 -= 2;
// 乘等运算
varName1 *= 2;
// 除等运算
varName1 /= 2;
// 余等运算
varName1 %= 2;
// 幂等运算
varName1 **= 1;
// 左移位运算
varName1 <<= 1;
// 右移位运算
varName1 >>= 1;
// 无符号右移位运算
varName1 >>>= 1; // 对于正数不变，负数先取反，在右移
/**
 * 按位与运算
 *   00000000000000000000000000000101
 * & 00000000000000000000000000000011
 * = 00000000000000000000000000000001
 * 上下两位都是1，最后结果才是1
 */
varName1 &= 1;
/**
 * 按位或运算
 *   00000000000000000000000000000101
 * | 00000000000000000000000000000011
 * = 00000000000000000000000000000111
 * 上下两位有1就是1，没1就是0
 */
varName1 |= 1;
/**
 * 按位异或运算
 *   00000000000000000000000000000101
 * ^ 00000000000000000000000000000011
 * = 00000000000000000000000000000110
 * 上下两位相同则为0，不同则为1
 */
varName1 ^= 1;
/**
 * 且等
 * varName1 = varName1 && 1
 * 如果 varName1 为真，则将 1 赋值给 varName1
 * 如果 varName1 为假，则 varName1 的值保持不变
 */
varName1 &&= 1;
/**
 * 或等
 * varName1 = varName1 || 1
 * 如果 varName1 为真，则 varName1 的值保持不变
 * 如果 varName1 为假，则将 1 赋值给 varName1
 */
varName1 ||= 1;
/**
 * 当 varName1 的值为空值或未定义（null 或 undefined）的时候，会被赋予等号后面的值
 * 一般用在给【配置数据】初始化时
 */
varName1 ??= 1;

// 解构
var arr10 = [1, 2, 3];
// 数组解构
let [arr0, arr1, arr2] = arr10;
console.log(arr0 === arr10[0]);
console.log(arr1 === arr10[1]);
console.log(arr2 === arr10[2]);

var obj10 = {
    name: 'bob',
    fn() {
    }
};
// 对象解构
let {name, fn} = obj10
fn(name);

/**
 * 2. 比较运算符
 */
// == 等于
console.log(1 == '1'); // true
// != 不等于
// === 等价于：更严格
console.log(1 === '1'); // false
// !== 不等价于：更严格
// > 大于
// >= 大于等于
// < 小于
// <= 小于等于

/**
 * 3. 算数运算符
 */
// + 加法
// - 减法
// * 乘法
// / 除法
// ++ 自增1
// -- 自减1
// + 一元正值符
console.log(+'3');
// - 一元负值符
console.log(-'3');
let x = 3;
console.log(-x); // -3
// ** 幂运算（指数运算）计算底数（base）的指数（exponent）次方，表示为 base^exponent。
console.log(4 ** 2); // 16 也就是 4 的 2 次方

/**
 * 4. 位运算符
 */
let a = 5; // 00000000000000000000000000000101
let b = 3; // 00000000000000000000000000000011
let c = -3; // 00000000000000000000000000000011

// 左移
a << 2;
// 00000000000000000000000000000101
// 00000000000000000000000000010100 向左移了两位，空位补0

// 右移
a >> 2;
// 00000000000000000000000000000101
// 00000000000000000000000000000001 向右移了两位，空位补0

// 无符号右移：先取反，再右移
c >>> 2;
// -3 负数的无符号右移过程
// 00000000 00000000 00000000 00000011 原始值 -3
// 11111111 11111111 11111111 11111100 先取反
// 11111111 11111111 11111111 11111101 再补码+1
// 00111111 11111111 11111111 11111111 右移2位，空位补0
// 1073741823
// 操作系统、网络安全、加密算法

// 按位与
a & b;
// 00000000000000000000000000000101 a
// 00000000000000000000000000000011 b
// 00000000000000000000000000000001 上下两位都是1，最后结果才是1

// 按位或
a | b
// 00000000000000000000000000000101 a
// 00000000000000000000000000000011 b
// 00000000000000000000000000000111 上下两位只要有1，结果就是1

// 按位异或
a ^ b;
// 00000000000000000000000000000101 a
// 00000000000000000000000000000011 b
// 00000000000000000000000000000110
// 上下两位只要有1，结果就是1，但还不能全是1，全是1结果还是0
// 也就是说上下两位不相等的时候才是1

// 按位非
~a;
// 对于整数来说：~3 = -3 - 1 = -4
// 对于负数来说：~(-3) = 3 - 1 = 2

/**
 * 5. 逻辑运算符
 */
if (1 && 0) {}
if (1 || 0) {}
if (!1) {}

// false && anything // 被短路求值为 false
// true || anything // 被短路求值为 true

/**
 * 6. 字符串运算符
 */
console.log('a' + 'b'); // 'ab'

/**
 * 7. 条件运算符（三元运算符、三目运算符）
 * 变量 = 条件 ? 值1 : 值2
 * 条件为真 则 变量 = 值1
 * 条件为假 则 变量 = 值2
 */
//


/**
 * 8. 逗号操作符
 */
const arr11 = [1, 3, 4, 5, 6, 7];
const obj11 = { name: 'bob', age: 12 }

/**
 * 9. 一元运算符
 */
delete obj11.name; // 删除对象中的一个属性
delete arr11[1]; // arr11 = [1, 空, 4, 5, 6, 7]
console.log(typeof obj11.age); // number

/**
 * 10. 关系运算符
 */
for (const key in obj11) {}
if ('name' in obj11) {} // 判断某个属性，是否存在于对象当中
if (4 in arr11) {} // 判断某个元素，是否存在于数组当中

// instanceof 是专门给对象用的，用来判断某一个对象，是否属于某个类
class Animal {}
class Dog extends Animal {}
const dog = new Dog();
const animal = new Animal();
console.log(dog instanceof Dog); // true
console.log(animal instanceof Animal); // true
console.log(dog instanceof Animal); // true 如果类存在继承关系，那么该对象也属于父类对象

// 运算符优先级，相当重要